/**
 * 
 */
package org.metaverse.warehouse.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandler;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParser;
import org.openrdf.rio.Rio;
import org.singularity.io.CloseablePipe;
import org.singularity.io.util.PrintPipe;

/**
 * @author vjache
 *
 */
public class RDFParseUtil {

	private static class RDFHandlerImpl extends RDFHandlerAdapter
	{
		private final CloseablePipe<Statement> _pipe;
		private RDFHandlerImpl(CloseablePipe<Statement> aPipe)
		{_pipe = aPipe;}
		@Override
		public void handleStatement(Statement arg0) throws RDFHandlerException {
			try 
			{_pipe.push(arg0);}
			catch (IOException e) 
			{throw new RDFHandlerException(e);}
		}
		@Override
		public void endRDF() throws RDFHandlerException {
			try 
			{_pipe.close();} 
			catch (IOException e) 
			{throw new RDFHandlerException(e);}
		}
	}
	private static class RDFHandlerListImpl extends ArrayList<Statement> implements RDFHandler
	{

		/**
		 * 
		 */
		private static final long serialVersionUID = 2770152870822758901L;

		@Override
		public void endRDF() throws RDFHandlerException {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void handleComment(String arg0) throws RDFHandlerException {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void handleNamespace(String arg0, String arg1)
				throws RDFHandlerException {
			
		}

		@Override
		public void handleStatement(Statement arg0) throws RDFHandlerException {
			add(arg0);
		}

		@Override
		public void startRDF() throws RDFHandlerException {
			// TODO Auto-generated method stub
			
		}
		
	}
	
	private RDFParseUtil() {}
	
	public static void main(String[] args) throws Exception{
		
		final URL url = new URL("http://dbpedia.org/data/Dmitry_Medvedev.rdf");
//		final URL url = new File("D:/_Development/R&D.wsp/rdf-ident/src/test/resources/tests-cmaps/dict-test/after-dict-2.ttl").toURI().toURL();
		RDFParseUtil.createTask(url, new PrintPipe<Statement>()).run();
		
	}
	
	public static Callable<List<Statement>> createCall(
			final String aText,
			final RDFFormat aFormat)
	{
		return new Callable<List<Statement>>() {
			@Override
			public List<Statement> call() throws Exception {
				final RDFHandlerListImpl list = new RDFHandlerListImpl();
				try
				{
					final RDFParser p = Rio.createParser(aFormat);
					final StringReader sr = new StringReader(aText);
					p.setRDFHandler(list);
					p.parse(sr, "");
				}
				catch(Exception e)
				{throw new RuntimeException(e);}
				return list;
			}
		};
	}
	public static Runnable createTask(
			final String aText,
			final RDFFormat aFormat, 
			final CloseablePipe<Statement> aPipe)
	{
		return new Runnable() {
			@Override
			public void run() {
				try
				{
					final RDFParser p = Rio.createParser(aFormat);
					final StringReader sr = new StringReader(aText);
					p.setRDFHandler(new RDFHandlerImpl(aPipe));
					p.parse(sr, null);
				}
				catch(Exception e)
				{throw new RuntimeException(e);}
			}
		};
	}
	public static Runnable createTask(
			URL aUrl,
			CloseablePipe<Statement> aConsumer)
	{
		return createTask(aUrl, null, aConsumer);
	}
	public static Runnable createTask(
			final URL aUrl, 
			final RDFFormat aFormat, 
			final CloseablePipe<Statement> aPipe)
	{
		return new Runnable() {
			
			@Override
			public void run() {
				InputStream stream = null;
				try
				{
					final URLConnection conn = aUrl.openConnection();
					final String ctxStr = aUrl.toURI().toString();
					final RDFParser p;
					if(aFormat!=null)
						p = Rio.createParser(aFormat);
					else
					{
						RDFFormat fmt = RDFFormat.forFileName(aUrl.toString());
						if(fmt==null)
						{
							final String ct = conn.getContentType();
							final String[] split = ct.split(";");
							fmt = RDFFormat.forMIMEType(split[0].toString());
							if(fmt==null)
								throw new IllegalArgumentException(String.format("Can not determine RDF format for '%s'.", aUrl));
						}
						p = Rio.createParser(fmt);
					}
					stream = new BufferedInputStream(conn.getInputStream());
					p.setRDFHandler(new RDFHandlerImpl(aPipe));
					p.setValueFactory(new ValueFactoryImpl(){
						@Override
						public Statement createStatement(Resource subject,
								URI predicate, Value object) {
							return super.createStatement(subject, predicate, object,createURI(ctxStr));
						}
					});
					p.parse(stream, ctxStr);
				}
				catch(Exception e)
				{throw new RuntimeException(e);}
				finally
				{
					if(stream!=null)
						try
						{stream.close();}
						catch (IOException e)
						{e.printStackTrace();}
				}
			}
		};
	}
	
	public static Runnable createTask(
			File aFile, 
			RDFFormat aFormat, 
			CloseablePipe<Statement> aPipe) throws MalformedURLException
	{
		return createTask(aFile.toURI().toURL(), aFormat, aPipe);
	}
	
	public static Runnable createTask(
			File aFile,  
			CloseablePipe<Statement> aPipe) throws MalformedURLException
	{
		return createTask(aFile.toURI().toURL(), aPipe);
	}

}
